package com.boot2.core.web;

import com.boot2.core.BaseEntity;
import com.boot2.core.BeanDefinitionSanner;
import com.boot2.core.utils.AesUtil;
import com.boot2.core.conf.UrlProperties;
import com.boot2.core.utils.StringUtils;
import lombok.Data;
import lombok.extern.apachecommons.CommonsLog;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.ModelAndView;

import javax.persistence.Table;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.InvocationTargetException;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

/**
 * 系统级拦截,所有全拦
 * 
 * @author zhangweilin
 * @date 2016年4月19日 下午12:15:07
 * @version V1.0
 * @description
 */
@Component
@CommonsLog
@Data
public class BusinessInterceptor extends AbstractBusinessInterceptor {

	/**
	 * 从session中取值的key
	 */
	private String sessionKey;
	/**
	 * 获取环境信息
	 */
	@Autowired
	Environment env;

	public BusinessInterceptor() {
	}

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		String[] activeProfilesArr = env.getActiveProfiles();
		String[] defaultProfilesArr = env.getDefaultProfiles();

		System.out.println("activeProfilesArr: " + ArrayUtils.toString(activeProfilesArr));
		System.out.println("defaultProfilesArr: " + ArrayUtils.toString(defaultProfilesArr));

		boolean flag = decipherPathVariableId(request, handler);
		if (!flag) {
			return flag;
		}
		return true;
	}

	/**
	 * id解密,只解析PathVariable的id
	 * 
	 * @param request
	 * @param handler
	 * @return
	 * @throws NoSuchMethodException
	 * @throws IllegalAccessException
	 * @throws InvocationTargetException
	 */
	private boolean decipherPathVariableId(HttpServletRequest request, Object handler)
			throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
		if (handler instanceof HandlerMethod) {
			HandlerMethod handlerMethod = (HandlerMethod) handler;
			System.out.println("方法名: "+handlerMethod.getMethod());
			String modelClassName = handlerMethod.getBean().getClass().getSimpleName();
			Object obj = request.getSession().getAttribute(sessionKey);
			if (obj == null) {
				return false;
			}
			String userName = (String) MethodUtils.invokeMethod(obj, "getUserName");
			if (modelClassName.contains("$")) {
				modelClassName = modelClassName.substring(0, modelClassName.indexOf("$"));
			}
			modelClassName = modelClassName.replace("Controller", "");
			modelClassName = StringUtils.changFirstWord(modelClassName, StringUtils.toLowerCase);
			Map<String, String> map0 = BeanDefinitionSanner.getBeanDefinition(modelBasePackageName, Table.class);
			if (!map0.containsKey(modelClassName)) {
				return true;
			}
			String key = userName + "_" + modelClassName + "_" + UrlProperties.aesEncryptKey;
			Map map = (Map) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
			log.info("接收@PathVariable:"+map);
//			if (true) {
//				return true;
//			}
			// 优先加密解密方案
			// 遍历的参数列表
			Set<Entry<String, String>> entrySet = map.entrySet();
			for (Entry<String, String> entry : entrySet) {
				String pathParam = entry.getKey();
				String pathValue = entry.getValue();
				request.setAttribute(pathParam + "Aes", pathValue);
				// 如果为数字，则走身份校验逻辑，反之，走加密解密逻辑
				if (StringUtils.isNumber(pathValue)) {
					// TODO 目前暂不考虑此方案
				} else {
					// 加密解密逻辑
					// 严格匹配当前model
					if ("id".equals(pathParam.toLowerCase()) || "parentId".equals(pathParam)) {
						String idStr = AesUtil.aesDecodeWithBase64(key, pathValue);
						Long id = Long.parseLong(idStr);
						log.info("解析出来的【" + modelClassName + "." + pathParam + "】: " + id);
						map.put(pathParam, id + "");
						request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, map);
						// 按model名匹配其它model,如productId,要匹配出product来，其中product为解密密钥的一部分
					} else {
						if (pathParam.matches(".*[iI][dD]")) {
							String modelName = pathParam.replaceAll("[iI][dD]", "");
							// modelName = StringUtils.changFirstWord(modelName, StringUtils.toLowerCase);
							// 如果提取出来的model名，在已注册的实体model类中，说明命名合法，否则，命名不合规范,严格抛异常
							if (map0.containsKey(modelName)) {
								// 提取出model名后，重新组装解密key,解出对应的model的正确id
								key = userName + "_" + modelName + "_" + UrlProperties.aesEncryptKey;
								String idStr = AesUtil.aesDecodeWithBase64(key, pathValue);
								Assert.isTrue(StringUtils.isNotEmpty(idStr),
										"无法解密参数，请确认参数是否正确，userName: " + userName + ",pathValue: " + pathValue
												+ ",modelName:" + modelName
												+ "，很有可能是参数不合规范，请严格按id或parentId或modelNameId的语法命名");
								Long id = Long.parseLong(idStr);
								log.info("解析出来的【" + modelName + "." + "Id】: " + id);
								map.put(pathParam, id + "");
								request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, map);
							} else {
								throw new IllegalArgumentException("参数对【 " + pathParam + "=" + pathValue
										+ "】不合规范，请严格按id或parentId或modelNameId的语法命名");
							}
						}
					}
				}
			}

			Set<String> keySet = new HashSet<>();
			keySet.add(key);

			MethodParameter[] methodParametersArr = handlerMethod.getMethodParameters();
			for (MethodParameter methodParameter : methodParametersArr) {
				String name = methodParameter.getParameterName();
				Class<?> clazz = methodParameter.getParameterType();

				if (BaseEntity.class.isAssignableFrom(clazz)) {
					modelClassName = clazz.getSimpleName();
					modelClassName = StringUtils.changFirstWord(modelClassName, StringUtils.toLowerCase);

					key = userName + "_" + modelClassName + "_" + UrlProperties.aesEncryptKey;
					keySet.add(key);
					System.out.println("name: " + name);
					System.out.println("clazz: " + clazz);
				}
			}
			request.setAttribute(modelBasePackageName + "_ncryptKey", keySet);
		}
		return true;
	}

	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
	}

	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {

	}

}
